﻿using System;
using System.Collections.Generic;
using CoreVirtuoso;
using CoreVirtuoso.Descriptions;
using VDS.RDF.Query;
using VDS.RDF.Storage;
using CoreVirtuoso.Helpers;
using VirtuosoDal.Config;
using VirtuosoDal.Helpers;
using VirtuosoDal.Repositories.RdfRepositories.Interfaces.GeoNames;

namespace VirtuosoDal.Repositories.RdfRepositories.GeoNames
{
   public class GeoNamesRepository : IGeoNamesRepository
   {
      public IEnumerable<CityDescription> GetCities(string text, string language)
      {
         if (String.IsNullOrEmpty(text))
            return new List<CityDescription>();

         string query = "prefix geo:           <http://www.w3.org/2003/01/geo/wgs84_pos#> " +
                        "prefix geonames:      <http://www.geonames.org/ontology#> " +
                        "SELECT DISTINCT ?city " +
                        "  (MIN(?cityName)) " +
                        "  (MIN(?cityPreferredName)) " +
                        "  (MIN(?cityAlternateName)) " +
                        "  (MIN(?countryName)) " +
                        "  (MIN(?countryPreferredName)) " +
                        "  (MIN(?countryAlternateName)) " +
                        "  (MIN(?latitude)) " +
                        "  (MIN(?longitude)) " +
                        "  ?population " +
                        "FROM <http://www.geonames.org#> " +
                        "WHERE " +
                        "{ " +
                        "  { " +
                        "    ?city geonames:name ?cityNameToSearch . " +
                        "    ?cityNameToSearch bif:contains '@cityQuery' . " +
                        "  } " +
                        "  UNION " +
                        "  { " +
                        "    ?city geonames:preferredName ?cityPreferredNameToSearch . " +
                        "    ?cityPreferredNameToSearch bif:contains '@cityQuery' . " +
                        "  } " +
                        "  UNION " +
                        "  { " +
                        "    ?city geonames:alternateName ?cityAlternateNameToSearch . " +
                        "    ?cityAlternateNameToSearch bif:contains '@cityQuery' . " +
                        "  } " +
                        "  ?city geonames:featureClass geonames:P . " +
                        "  ?city geonames:name ?cityName . " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?city geonames:preferredName ?cityPreferredName . " +
                        "    FILTER (lang(?cityPreferredName) = @lang) " +
                        "  } " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?city geonames:alternateName ?cityAlternateName . " +
                        "    FILTER (lang(?cityAlternateName) = @lang) " +
                        "  } " +
                        "  ?city geonames:inCountry ?country . " +
                        "  ?country geonames:name ?countryName . " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?country geonames:preferredName ?countryPreferredName . " +
                        "    FILTER (lang(?countryPreferredName) = @lang) " +
                        "  } " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?country geonames:alternateName ?countryAlternateName . " +
                        "    FILTER (lang(?countryAlternateName) = @lang) " +
                        "  } " +
                        "  ?city geo:lat ?latitude . " +
                        "  ?city geo:long ?longitude . " +
                        "  ?city geonames:population ?population . " +
                        "} " +
                        "ORDER BY DESC (?population)";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.GeoNamesOntologyNamespace));
         sparqlParameterizedString.SetLiteral("@cityQuery", text);
         sparqlParameterizedString.SetLiteral("@lang", language);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager,
                                                                sparqlParameterizedString.ToString(),
                                                                null);

            return SparqlResultSetConverter.ToListOfGeoNamesCityDescription(sparqlResultSet);
         }
      }

      public IEnumerable<GeoNamesObjectDescription> GetGeoObjects(string text, string language)
      {
         if (text == null)
            return new List<GeoNamesObjectDescription>();

         string query = "prefix geonames:      <http://www.geonames.org/ontology#> " +
                        "SELECT DISTINCT ?geoObject " +
                        "  (MIN(?geoObjectName)) " +
                        "  (MIN(?geoObjectPreferredName)) " +
                        "  (MIN(?geoObjectAlternateName)) " +
                        "  ?isCountry " +
                        "  ?population " +
                        "FROM <http://www.geonames.org#> " +
                        "WHERE " +
                        "{ " +
                        "  { " +
                        "    ?geoObject geonames:name ?geoObjectNameToSearch . " +
                        "    ?geoObjectNameToSearch bif:contains '@text' . " +
                        "  } " +
                        "  UNION " +
                        "  { " +
                        "    ?geoObject geonames:preferredName ?geoObjectPreferredNameToSearch . " +
                        "    ?geoObjectPreferredNameToSearch bif:contains '@text' . " +
                        "  } " +
                        "  UNION " +
                        "  { " +
                        "    ?geoObject geonames:alternateName ?geoObjectAlternateNameToSearch . " +
                        "    ?geoObjectAlternateNameToSearch bif:contains '@text' . " +
                        "  } " +
                        "  ?geoObject geonames:name ?geoObjectName . " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?geoObject geonames:preferredName ?geoObjectPreferredName . " +
                        "    FILTER (lang(?geoObjectPreferredName) = @lang) " +
                        "  } " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?geoObject geonames:alternateName ?geoObjectAlternateName . " +
                        "    FILTER (lang(?geoObjectAlternateName) = @lang) " +
                        "  } " +
                        "  ?geoObject geonames:population ?population . " +
                        "  OPTIONAL " +
                        "  { " +
                        "    ?geoObject geonames:isCountry ?isCountry . " +
                        "  } " +
                        "} " +
                        "ORDER BY DESC (?population)";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.GeoNamesOntologyNamespace));
         sparqlParameterizedString.SetLiteral("@text", text);
         sparqlParameterizedString.SetLiteral("@lang", language);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager,
                                                                sparqlParameterizedString.ToString(),
                                                                null);

            return SparqlResultSetConverter.ToListOfGeoObjects(sparqlResultSet);
         }
      }

      public List<GeoNamesCityShortDescription> GetGeoNamesShortCitiesDescriptions(List<string> geoObjectsUris,
                                                                                   string language)
      {
         if (geoObjectsUris == null || geoObjectsUris.Count == 0)
            return new List<GeoNamesCityShortDescription>();

         string urisEnumerationString = UriEnumerationHelper.GetUriEnumerationString(geoObjectsUris.Count);

         string query = String.Format("prefix geo:           <http://www.w3.org/2003/01/geo/wgs84_pos#> " +
                                      "prefix geonames:      <http://www.geonames.org/ontology#> " +
                                      "SELECT DISTINCT ?city max(?cityName) max(?cityPreferredName) max(?cityAlternateName) " +
                                      "  max(?countryName) max(?countryPreferredName) max(?countryAlternateName) " +
                                      "FROM <http://www.geonames.org#> " +
                                      "WHERE " +
                                      "{{ " +
                                      "  ?city geonames:name ?cityName . " +
                                      "  OPTIONAL " +
                                      "  {{ " +
                                      "    ?city geonames:preferredName ?cityPreferredName . " +
                                      "    FILTER (lang(?cityPreferredName) = @lang) " +
                                      "  }} " +
                                      "  OPTIONAL " +
                                      "  {{ " +
                                      "    ?city geonames:alternateName ?cityAlternateName . " +
                                      "    FILTER (lang(?cityAlternateName) = @lang) " +
                                      "  }} " +
                                      "  ?city geonames:inCountry ?country . " +
                                      "  ?country geonames:name ?countryName . " +
                                      "  OPTIONAL " +
                                      "  {{ " +
                                      "    ?country geonames:preferredName ?countryPreferredName . " +
                                      "    FILTER (lang(?countryPreferredName) = @lang) " +
                                      "  }} " +
                                      "  OPTIONAL " +
                                      "  {{ " +
                                      "    ?country geonames:alternateName ?countryAlternateName . " +
                                      "    FILTER (lang(?countryAlternateName) = @lang) " +
                                      "  }} " +
                                      "  FILTER " +
                                      "  ( " +
                                      "    ?city in ({0}) " +
                                      "  ) " +
                                      "}} ", urisEnumerationString);

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.GeoNamesOntologyNamespace));
         sparqlParameterizedString.SetLiteral("@lang", language);
         UriEnumerationHelper.FillUriEnumerationStringParameters(sparqlParameterizedString, geoObjectsUris);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager,
                                                                sparqlParameterizedString.ToString(),
                                                                null);

            return SparqlResultSetConverter.ToListOfGeoNamesShortCityDescriptions(sparqlResultSet);
         }
      }

      public string GetCityTimeZone(string cityUri)
      {
         string query = "prefix geonames:      <http://www.geonames.org/ontology#> " +
                        "SELECT DISTINCT ?timezone " +
                        "FROM <http://www.geonames.org#> " +
                        "WHERE " +
                        "{ " +
                        "  @city geonames:timezone ?timezone . " +
                        "} " +
                        "LIMIT 1";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.GeoNamesOntologyNamespace));
         sparqlParameterizedString.SetUri("@city", new Uri(cityUri));

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager,
                                                                sparqlParameterizedString.ToString(),
                                                                null);

            return SparqlResultSetConverter.ToTimeZone(sparqlResultSet);
         }
      }
   }
}